from pyaudio import PyAudio, paInt16
import logging
import threading
import numpy as np
import cv2
import time



class VideoServer(object):
    def __init__(self):
        self.topic = ""
        self.buffer = []
        self.video_frame = 0.04
        self.send_frame = self.video_frame
        self.should_stop_rec = True
        self.should_stop_pub = True

    def start(self, client):
        self.client = client
        self.start_rec()
        self.start_pub()

    def stop(self):
        self.stop_pub()
        self.stop_rec()
        self.client = None
        self.topic = ""

    def start_rec(self):
        self.should_stop_rec = False
        self.capture = cv2.VideoCapture(0)
        logging.info('video start rec')
        self.record()

    def record(self):
        logging.debug('video recing')
        _, frame = self.capture.read()
        frame = cv2.imencode('.jpg', frame)[1]
        logging.debug('video rec1:' + str(len(self.buffer)))
        self.buffer.append(frame)
        logging.debug('video rec2:' + str(len(self.buffer)))
        if not self.should_stop_rec:
            self.t1 = threading.Timer(self.video_frame, self.record)
            self.t1.start()

    def stop_rec(self):
        self.should_stop_rec = True
        self.t1.cancel()
        self.buffer = []
        del self.capture

    def start_pub(self):
        self.should_stop_pub = False
        logging.info('video start pub')
        self.publish()

    def publish(self):
        logging.debug("video buffer:" + str(len(self.buffer)))
        if self.buffer != []:
            logging.debug('video pubing')
            if len(self.buffer) == 0:
                data = ""
            else:
                data = self.buffer.pop(0).tostring()
            self.client.publish(topic=self.topic, payload=data, qos=2)
            logging.debug('video pubbed')
        if not self.should_stop_pub:
            self.t2 = threading.Timer(
                self.send_frame, self.publish)
            self.t2.start()

    def stop_pub(self):
        logging.info('video stop pub')
        self.should_stop_pub = True
        self.t2.cancel()


class AudioServer(object):
    def __init__(self):
        self.topic = ""

        self.SAMPLING_RATE = 8000
        self.AUDIO_FRAME = 0.12
        self.SEND_FRAME = self.AUDIO_FRAME
        self.NUM_SAMPLES = int(self.AUDIO_FRAME * self.SAMPLING_RATE)

        self.buffer = []
        self.should_stop_rec = True
        self.should_stop_pub = True
        self.p = PyAudio()

    def __del__(self):
        self.p.terminate()

    def start(self, client):
        self.buffer = []
        self.stream = self.p.open(format=paInt16, channels=1,
                                  rate=self.SAMPLING_RATE,
                                  input=True, frames_per_buffer=self.NUM_SAMPLES)
        self.client = client
        self.start_rec()
        self.start_pub()

    def stop(self):
        self.stop_pub()
        self.stop_rec()
        # self.stream.stop_stream()
        self.stream.close()
        self.client = None
        self.topic = ""

    def start_rec(self):
        self.should_stop_rec = False
        logging.info('audio start rec')
        t = threading.Timer(0, self.record)
        t.start()

    def record(self):
        while not self.should_stop_rec:
            logging.debug('audio recing')
            logging.debug('audio rec1:' + str(len(self.buffer)))
            self.buffer.append(self.stream.read(self.NUM_SAMPLES))
            logging.debug('audio rec2:' + str(len(self.buffer)))

    def stop_rec(self):
        self.should_stop_rec = True
        logging.info('audio stop rec')
        self.buffer = []

    def start_pub(self):
        self.should_stop_pub = False
        logging.info('audio start pub')
        t = threading.Timer(0, self.publish)
        t.start()

    def publish(self):
        while not self.should_stop_pub:
            logging.debug("audio buffer:" + str(len(self.buffer)))
            if self.buffer != []:
                logging.debug('audio pubing')
                data = self.buffer.pop(0)
                self.client.publish(topic=self.topic, payload=data, qos=2)
                logging.debug('audio pubbed')
            else:
                time.sleep(self.AUDIO_FRAME)

    def stop_pub(self):
        logging.info('audio stop pub')
        self.should_stop_pub = True
        self.t2.cancel()


class VideoClient(object):
    def __init__(self):
        self.name = "friend"
        self.lastframe = None

    def play_frame(self, data):
        if data != "":
            self.lastframe = data
        elif self.lastframe != None:
            data = self.lastframe
        nparr = np.fromstring(data, np.uint8)
        return cv2.imdecode(nparr, cv2.IMREAD_COLOR)

    def stop(self):
        cv2.destroyAllWindow()


class AudioClient(object):
    def __init__(self):
        self.SAMPLING_RATE = 8000
        self.p = PyAudio()
        self.stream = self.p.open(
            format=paInt16, channels=1, rate=self.SAMPLING_RATE, output=True)
        self.buffer = []

    def play_frame(self, data):
        self.buffer.append(data)

    def play(self):
        self.playing = True
        t = threading.Timer(0, self.play_async)
        t.start()

    def stop(self):
        self.playing = False
        self.buffer = []

    def play_async(self):
        while self.playing:
            if len(self.buffer) > 0:
                self.stream.write(bytes(self.buffer.pop(0)))
            else:
                time.sleep(0.1)
